package com.ymx.darling.expression;

import com.ymx.darling.exception.ExceptionFactory;
import com.ymx.darling.exception.ExpressionException;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 逻辑表达式处理器
 * @author 爱Java的小于
 */
public class LogicExpressionEvaluator {
    /**
     * 保存运算符的优先级
     */
    private static final Map<String, Integer> priorityMap = new HashMap<>();
    static {
        priorityMap.put(">", 2);
        priorityMap.put("lt", 2);
        priorityMap.put(">=", 2);
        priorityMap.put("le", 2);
        priorityMap.put("==", 2);
        priorityMap.put("!=", 2);
        priorityMap.put("and", 1);
        priorityMap.put("or", 1);
        priorityMap.put("(", 0);
    }

    /**
     * 表达式访问器
     */
    private final ExpressionAccess expressionAccess;
    /**
     * 表达式解析器
     */
    private final TokenParser tokenParser = new TokenParser();

    public LogicExpressionEvaluator(Object context) {
        this.expressionAccess = new ExpressionAccess(context);
    }


    /**
     * 计算逻辑字符串表达式
     * @param expression 字符串表达式
     * @return boolean 表达式结果只能是布尔型
     */
    public boolean eval(String expression) {
        try {
            List<Token> tokens = tokenParser.parse(expression);
            // 保存操作数或数据
            LinkedStack<Object> dataStack = new LinkedStack<>();
            // 保存运算符
            LinkedStack<String> codeStack = new LinkedStack<>();

            for (Token token : tokens) {
                if (isOperand(token)) {
                    Object value = this.parseValue(token);
                    dataStack.push(value);
                } else {
                    this.operatorHandle(token, dataStack, codeStack);
                }
            }

            // 处理剩余的运算符
            while (!codeStack.isEmpty()) {
                compute(dataStack, codeStack.pop());
            }

            // 数据栈中只能有一个结果值
            if (dataStack.size() != 1) {
                throw new ExpressionException("错误的表达式结构: 数据栈中剩余多个值");
            }

            Object result = dataStack.pop();
            if (!(result instanceof Boolean)) {
                throw new ExpressionException("表达式的结果必须是布尔值，但实际结果为: " + result.getClass().getName());
            }

            return (boolean) result;
        } catch (Exception exception) {
            throw ExceptionFactory.throwStrategy(exception, new ExpressionException(exception, "表达式" + expression + "解析出错:" + exception.getMessage()));
        }
    }


    /**
     * 运算符处理
     * @param operatorToken 运算符的token
     * @param dataStack 数据栈
     * @param codeStack 运算符栈
     */
    private void operatorHandle(Token operatorToken, LinkedStack<Object> dataStack, LinkedStack<String> codeStack) {
        String operator = operatorToken.getValue();

        if (operator.equals("(")) {
            codeStack.push(operator);
        } else if (operator.equals(")")) {
            while (!codeStack.isEmpty() && !codeStack.peek().equals("(")) {
                compute(dataStack, codeStack.pop());
            }
            if (!codeStack.isEmpty() && codeStack.peek().equals("(")) {
                codeStack.pop(); // 弹出左括号
            } else {
                throw new ExpressionException("括号不匹配,缺少括号:(");
            }
        } else {
            while (!codeStack.isEmpty() && getPriority(codeStack.peek()) >= getPriority(operator)) {
                compute(dataStack, codeStack.pop());
            }
            codeStack.push(operator);
        }
    }


    /**
     * 从数据栈中取出两个操作数并计算，将结果放入数据栈中
     * @param dataStack 数据栈
     * @param code 比较运算符
     */
    private void compute(LinkedStack<Object> dataStack, String code) {
        if (code.equals("(") || code.equals(")")) {
            throw new ExpressionException("括号不匹配,缺少括号:"+(code.equals("(") ? ")" : "("));
        }
        if (dataStack.size() < 2) {
            throw new ExpressionException("操作数不足,无法完成运算.运算符: " + code);
        }

        Object two = dataStack.pop();
        Object one = dataStack.pop();

        boolean result;
        try {
            switch (code) {
                case ">":
                    result = Calculate.greaterThan(one, two);
                    break;
                case ">=":
                    result = Calculate.greaterThanOrEqualTo(one, two);
                    break;
                case "lt":
                    result = Calculate.lessThan(one, two);
                    break;
                case "le":
                    result = Calculate.lessThanOrEqualTo(one, two);
                    break;
                case "==":
                    result = Calculate.equalTo(one, two);
                    break;
                case "!=":
                    result = Calculate.notEqualTo(one, two);
                    break;
                case "and":
                    result = Calculate.andTo(one, two);
                    break;
                case "or":
                    result = Calculate.orTo(one, two);
                    break;
                default:
                    throw new ExpressionException("无法识别运算符:" + code);
            }
            dataStack.push(result);
        } catch (Exception e) {
            throw new ExpressionException("运算错误:" + e.getMessage() + ".运算符: " + code + ", 操作数1: " + one + ", 操作数2: " + two);
        }
    }

    /**
     * 解析Token为指定的数据
     * @param token token
     * @return Object 解析后的数据
     */
    private Object parseValue(Token token) {
        try {
            switch (token.getType()) {
                case NULL:
                    return null;
                case NUMBER:
                    return Double.parseDouble(token.getValue());
                case BOOLEAN:
                    return Boolean.valueOf(token.getValue());
                case STRING:
                    return token.getValue().replaceAll("'", "");
                default:
                    return getValue(token.getValue());
            }
        } catch (Exception e) {
            throw ExceptionFactory.throwStrategy(e, new ExpressionException(e, "解析错误: " + e.getMessage() + ".Token: " + token.getValue()));
        }
    }

    /**
     * 返回指定引用的值，可以使用 . 引用多次
     * @param parameterName 参数名
     * @return Object
     */
    private Object getValue(String parameterName) {
        Object value = expressionAccess.getValue(parameterName);
        // 对于字符型数据需要转换为字符串
        return value instanceof Character ? value.toString() : value;
    }

    /**
     * 返回运算符的优先级
     * @param comparisonOperator 比较运算符
     * @return int
     */
    public int getPriority(String comparisonOperator) {
        if (!priorityMap.containsKey(comparisonOperator)) {
            throw new ExpressionException("未知的运算符: " + comparisonOperator);
        }
        return priorityMap.get(comparisonOperator);
    }

    /**
     * 判断是否是操作数
     * @param token token
     * @return boolean
     */
    private boolean isOperand(Token token) {
        return token.getType() != TokenType.OPERATOR && token.getType() != TokenType.BRACKET;
    }
}